/*
#Copyright (c) 2013, Philippe Bordron <philippe.bordron@gmail.com>
#
# This file is part of SIPPER.
#
# DCC is free software: you can redistribute it and/or modify
# it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DCC is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU LESSER GENERAL PUBLIC LICENSE for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# along with SIPPER.  If not, see <http://www.gnu.org/licenses/>
*/
package combi.wip.sipper.modeling.metabolicNetworks;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import combi.wip.sipper.modeling.reactions.Reaction;
import combi.wip.sipper.modeling.reactions.ReactionUtil;

public  class ImplOfMetabolicNetwork<C,R extends Reaction<C>> extends MetabolicNetwork<C,R> {

	protected Map<C, Collection<Reaction<C>>> substratToReactions;
	protected Map<C, Collection<Reaction<C>>> productToReactions;

	public ImplOfMetabolicNetwork()
	{
		substratToReactions = new HashMap<C, Collection<Reaction<C>>>();
		productToReactions = new HashMap<C, Collection<Reaction<C>>>();
		super.init();
	}
	
	public ImplOfMetabolicNetwork(MetabolicNetwork<C,Reaction<C>> meta)
	{
		this.substratToReactions = new HashMap<C, Collection<Reaction<C>>>();
		this.productToReactions = new HashMap<C, Collection<Reaction<C>>>();
		super.deepCopy(meta);
	}
	
	@Override
	public void addCompound(C cpd) {
		this.compoundSet.put(cpd.toString(), cpd);
	}

	@Override
	public void removeCompound(C cpd) {
		List<Reaction<C>> toRemove = new ArrayList<Reaction<C>>(this.getReactions(cpd));
		for(Reaction<C> r : toRemove)
		{
			this.removeReaction(r);
		}
		this.compoundSet.remove(cpd);
	}

	private void addToMapCToSetR(Map<C, Collection<Reaction<C>>> map, C c, Reaction<C> r)
	{
		Collection<Reaction<C>> set =  map.get(c);
		if (set == null)
		{
			set = new HashSet<Reaction<C>>();
			map.put(c, set);
		}
		set.add(r);
	}
	
	private void removeFromMapCToSetR(Map<C, Collection<Reaction<C>>> map, C c, Reaction<C> r)
	{
		Collection<Reaction<C>> set = map.get(c);
		if (set != null)
		{
			set.remove(r);
			if (set.isEmpty())
			{
				map.remove(c);
			}
		}
	}
	
	@Override
	public void addReaction(Reaction<C> reaction) {
		for (C c : reaction.getSubstrats())
		{
			this.addCompound(c);
			this.addToMapCToSetR(this.substratToReactions, c, reaction);
		}
		for (C c : reaction.getProducts())
		{
			this.addCompound(c);
			this.addToMapCToSetR(this.productToReactions, c, reaction);
		}
		this.reactionSet.put(reaction.getId(), reaction);
	}

	@Override
	public void removeReaction(Reaction<C> reaction) {
		Collection<C> toRemove = this.getSubstrats(reaction);
		for(C c : toRemove)
		{
			removeFromMapCToSetR(this.substratToReactions, c, reaction);
		}
		toRemove = this.getProducts(reaction);
		for(C c : toRemove)
		{
			removeFromMapCToSetR(this.productToReactions, c, reaction);
		}
		this.reactionSet.remove(reaction);
	}

	@Override
	public Collection<C> getCompounds(Reaction<C> reaction) {
		return reaction.getCompounds();
	}
	
	@Override
	public Collection<C> getSubstrats(Reaction<C> reaction) {
		return reaction.getSubstrats();
	}
	
	@Override
	public Collection<C> getProducts(Reaction<C> reaction) {
		return reaction.getProducts();
	}

	@Override
	public Collection<Reaction<C>> getReactions(C cpd) {
		Collection<Reaction<C>> result = new HashSet<Reaction<C>>();
		result.addAll(this.whichProduce(cpd));
		result.addAll(this.whichConsume(cpd));
		return result;
	}

	@Override
	public Collection<Reaction<C>> whichProduce(C cpd) {
		Collection<Reaction<C>> result = new HashSet<Reaction<C>>();
		Collection<Reaction<C>> tmp =	this.productToReactions.get(cpd);
		if (tmp != null)
		{
			result.addAll(tmp);
		}
		return result;
	}

	@Override
	public Collection<Reaction<C>> whichConsume(C cpd) {
		Collection<Reaction<C>> result = new HashSet<Reaction<C>>();
		Collection<Reaction<C>> tmp =	this.substratToReactions.get(cpd);
		if (tmp != null)
		{
			result.addAll(tmp);
		}
		return result;
	}
	
	@Override
	public void clear()
	{
		super.clear();
		substratToReactions.clear();
		productToReactions.clear();
	}


	@Override
	public void updateReaction(Reaction<C> reaction) {
		removeReaction(reaction);
		addReaction(reaction);
	}
	
	public static void main(String[] args)
	{
		MetabolicNetwork<String, Reaction<String>> meta = new ImplOfMetabolicNetwork<String, Reaction<String>>();
		String c1 = "c1";
		meta.addCompound(c1);
		System.out.println("ajout dans meta : "+c1);
		System.out.println("meta : "+meta.toString());
		System.out.println(meta.toString());
		
		String c2 = "c2", c3 = "c3";
		Collection<String> pg = new HashSet<String>();
		pg.add(c1);
		Collection<String> pd = new HashSet<String>();
		pd.add(c2);pd.add(c3);
		Reaction<String> r1 = new Reaction<String>("r1", pg, pd, ReactionUtil.Orientation.LR);
		System.out.println("ajout dans meta : "+r1);
		meta.addReaction(r1);
		System.out.println("meta : "+meta.toString());
		//System.out.println("meta.getCompounds(r1) : "+meta.getCompounds(r1).toString());
		//System.out.println("meta.getReactions(c1) : "+meta.getReactions(c1).toString());
		//System.out.println("meta.whichConsume(c1) : "+meta.whichConsume(c1).toString());
		//System.out.println("meta.whichProduce(c1) : "+meta.whichProduce(c1).toString());
		
		
		System.out.println("copie de meta dans meta2 et meta3");
		MetabolicNetwork<String, Reaction<String>> meta2 = new ImplOfMetabolicNetwork<String, Reaction<String>>(meta);
		MetabolicNetwork<String, Reaction<String>> meta3 = new ImplOfMetabolicNetwork<String, Reaction<String>>(meta);
		System.out.println("meta : "+meta.toString());
		System.out.println("meta2 : "+meta2.toString());
		System.out.println("meta3 : "+meta3.toString());
//		System.out.println("meta3.getCompounds(r1) : "+meta3.getCompounds(r1).toString());
//		System.out.println("meta3.getReactions(c1) : "+meta3.getReactions(c1).toString());
//		System.out.println("meta3.whichConsume(c1) : "+meta3.whichConsume(c1).toString());
//		System.out.println("meta3.whichProduce(c1) : "+meta3.whichProduce(c1).toString());

		System.out.println("retrait de meta3 : "+c1);
		meta3.removeCompound(c1);
		System.out.println("meta : "+meta.toString());
		System.out.println("meta2 : "+meta2.toString());
		System.out.println("meta3 : "+meta3.toString());
		
		System.out.println("retrait de meta : "+c1);
		meta.removeCompound(c1);
		System.out.println("meta : "+meta.toString());
		System.out.println("meta2 : "+meta2.toString());

		System.out.println("retrait de meta 2 : "+r1);
		meta2.removeReaction(r1);
		System.out.println("meta : "+meta.toString());
		System.out.println("meta2 : "+meta2.toString());
		
		System.out.println("clear de meta 2 : "+r1);
		meta2.clear();
		System.out.println("meta : "+meta.toString());
		System.out.println("meta2 : "+meta2.toString());
		

	}
}
